using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using JigLibX.Physics;
using JigLibX.Geometry;
using JigLibX.Collision;


namespace Utilities
{
  public static class PhysicsHelper
  {
    public static Body create_physics_body(Vector3 _Pos, Matrix _Heading, float _Mass, Vector3 _Vel, Action<CollisionSkin> fill_collision_skins)
    {
      var body = new Body();
      var collision = new CollisionSkin(body);
      fill_collision_skins(collision);
      body.CollisionSkin = collision;
      body.Mass = _Mass;
      body.MoveTo(_Pos, _Heading);
      body.Velocity = _Vel;
      body.EnableBody();
      return body;
    }

    public static Body create_spheres(Vector3 _Pos, BoundingSphere _Parent, IEnumerable<BoundingSphere> _Children, float _Mass)
    {
      var _Radius = _Parent.Radius;
      return create_physics_body(_Pos, Matrix.Identity, _Mass, Vector3.Zero,
            (collision =>
            {
              foreach (var c in _Children)
                collision.AddPrimitive(new Sphere(c.Center, c.Radius),
                    new MaterialProperties(0.5f, 0.7f, 0.6f));
            }));
    }

    public static Body create_sphere(BoundingSphere _Parent, IEnumerable<BoundingSphere> _Children, Vector3 _Vel, float _Mass, IEnumerable<Body> non_collidables)
    {
      var _Pos = _Parent.Center;
      var _Radius = _Parent.Radius;
      var d0 = Vector3.Forward;
      var v = Vector3.Normalize(_Vel);
      var angle = (float)Math.Acos(Vector3.Dot(d0, v));
      var axis = Vector3.Cross(d0, v);
      var orientation = Matrix.CreateFromAxisAngle(axis, angle);
      return create_physics_body(_Pos, orientation, _Mass, _Vel,
            (collision =>
              {
                collision.AddPrimitive(new Sphere(Vector3.Zero, _Parent.Radius),
                    new MaterialProperties(0.5f, 0.7f, 0.6f));
                foreach (var nc in non_collidables)
                {
                  nc.CollisionSkin.NonCollidables.Add(collision);
                  collision.NonCollidables.Add(nc.CollisionSkin);
                }
              }));
    }

    public static Body create_projectile(Vector3 _Pos, float _Radius, Matrix _Heading, float _Speed, float _Mass, IEnumerable<Body> non_collidables)
    {
      return create_physics_body(_Pos, _Heading, _Mass, _Heading.Forward * _Speed,
            (collision =>
            {
              collision.AddPrimitive(new Sphere(Vector3.Zero, _Radius),
                  new MaterialProperties(0.5f, 0.7f, 0.6f));
              foreach (var nc in non_collidables)
              {
                nc.CollisionSkin.NonCollidables.Add(collision);
                collision.NonCollidables.Add(nc.CollisionSkin);
              }
            }));
    }
  }
}
